import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { HttpService } from '@nestjs/axios'
import axios, { AxiosResponse } from "axios";
import {
	IWxPhoneInfo,
	IWxPhoneResult,
	IWxSessionResult,
	IWxTokenResult,
	WxErrorCode,
	WxPhoneErrorCode,
	WxTokenErrorCode
} from "./types/wx.type";
import { env } from "~/global/env";

@Injectable()
export class WxService {
	private readonly logger = new Logger(WxService.name)
	private readonly httpService: HttpService

	private appId: string = env("APP_ID")

	private appSecret: string = env("APP_SECRET")

	/**
	 * 私有属性，用于存储稳定版接口调用凭据
	 */
	private token: string = ""

	/**
	 * 私有属性，用于存储稳定版接口调用凭据过期时间
	 */
	private tokenExpiresAt: number = 0

	/**
	 * 获取稳定版接口调用凭据
	 */
	async getStableAccessToken(): Promise<string> {
		const result: AxiosResponse<IWxTokenResult> = await axios.request({
			url: "https://api.weixin.qq.com/cgi-bin/stable_token",
			method: "POST",
			data: {
				appid: this.appId,
				secret: this.appSecret,
				grant_type: "client_credential",
				force_refresh: true
			}
		})

		if (Boolean(result.data.errcode) && result.data.errcode !== WxTokenErrorCode.Success) {
			throw new BadRequestException("获取微信access_token失败")
		}

		return result.data.access_token
	}

	/**
	 * 小程序登录
	 * @description 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
	 * @param code - 登录时获取的 code
	 */
	async getSession(code: string): Promise<IWxSessionResult> {
		const result: AxiosResponse<IWxSessionResult> = await axios.get(
			'https://api.weixin.qq.com/sns/jscode2session',
			{
				params: {
					appid: this.appId,
					secret: this.appSecret,
					js_code: code,
					grant_type: "authorization_code"
				}
			}
		)

		this.logger.debug(`getSession result: ${JSON.stringify(result.data)}`)

		if (result.data.errcode === WxErrorCode.INVALID) {
			throw new BadRequestException("js_code无效")
		}

		if (result.data.errcode === WxErrorCode.LIMIT) {
			throw new BadRequestException("API 调用太频繁，请稍候再试")
		}

		if (result.data.errcode === WxErrorCode.BLOCKED) {
			throw new BadRequestException("高风险等级用户，小程序登录拦截 。风险等级详见用户安全解方案")
		}

		if (result.data.errcode === WxErrorCode.SYSTEM_ERROR) {
			throw new BadRequestException("系统繁忙，此时请开发者稍候再试")
		}

		if (result?.data?.errcode && result?.data?.errcode !== WxErrorCode.SUCCESS) {
			throw new BadRequestException("获取微信session失败")
		}

		return result.data
	}

	async getUserPhoneNumber(code: string): Promise<IWxPhoneInfo> {
		const accessToken = await this.getStableAccessToken()

		const result: AxiosResponse<IWxPhoneResult> = await this.httpService.request({
			url: `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${accessToken}`,
			method: 'POST',
			data: {
				code,
				appId: this.appId
			}
		}).toPromise()

		if (result.data.errcode !== WxPhoneErrorCode.SUCCESS) {
			throw new BadRequestException("获取微信手机号失败")
		}

		return result.data.phone_info
	}

	/**
	 * 获取稳定版接口调用凭据
	 * @returns 凭据
	 */
	async getAccessToken(): Promise<string> {
		// 获取当前时间戳
		const now = Date.now()

		// 如果当前时间戳小于凭据过期时间，则直接返回凭据
		if (now < this.tokenExpiresAt) {
			return this.token
		}

		// 否则重新获取凭据
		this.token = await this.getStableAccessToken()

		// 设置凭据过期时间为当前时间戳加上凭据有效时间
		this.tokenExpiresAt = now + 7200 * 1000

		return this.token
	}

	/**
	 * 效验用户手机号
	 * @param code
	 * @returns
	 */
	async validatePhoneByCode(code: string): Promise<IWxPhoneInfo> {
		const token = await this.getAccessToken()

		const result: AxiosResponse<IWxPhoneResult> = await axios.post(
			`https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${token}`,
			{
				code
			}
		)

		if (Boolean(result.data.errcode) && result.data.errcode !== WxPhoneErrorCode.SUCCESS) {
			throw new BadRequestException("获取微信手机号失败")
		}

		return result.data.phone_info
	}
}
